using System;
using System.Collections.Generic;
using System.Text;
using Xeml.Manager;
using Mpi.Common.Collections;
using Xeml.Document.Contracts;
using System.Diagnostics;
using Xeml.Document;
using Xeml.Document.Ontology;
using Xeml.Document.EnvironmentalOntology;
using Xeml.Document.PlantStructure;
using Xeml.Document.Ontologies.OBO;
using Xeml.Document.Ontologies.Environment;
using System.Linq;
using System.Collections;
using System.Reflection;
using Xeml.Document.CoreObjects;

namespace Xeml.Manager
{
    public sealed class OntologyManager
    {
        #region Singleton pattern
        static readonly OntologyManager instance = new OntologyManager();
        private GeneralOntologyHandler generalOntologyHandler;

        static OntologyManager()
        {
        }

        OntologyManager()
        {
            this.OntologyHandler = new Dictionary<string, IOntologyHandler>();
            Init();
        }

        public static OntologyManager Instance
        {
            get
            {
                return instance;
            }
        }
        #endregion

        public delegate void MessageEmittedEventHandler(object sender, MessageEventArgs e);
        public event MessageEmittedEventHandler MessageEmitted;
 
        public void StatusUpdate(string message)
        {
            if (MessageEmitted != null)
            {
                MessageEmitted(this, new MessageEventArgs(message, EventLogEntryType.Information));
            }
        } 

        private void Init()
        {
            StatusUpdate("Pre-loading ontology handlers.");
            EnvironmentOntologyHandler poh = new EnvironmentOntologyHandler();
            this.OntologyHandler.Add(poh.Uri, poh);

            PlantStructureOntologyHandler pso = new PlantStructureOntologyHandler();
            this.OntologyHandler.Add(pso.Uri, pso);

            DevelopmentalOntologyHandler doh = new DevelopmentalOntologyHandler();
            this.OntologyHandler.Add(doh.Uri, doh);

            PositioningOntologyHandler posh = new PositioningOntologyHandler();
            this.OntologyHandler.Add(posh.Uri, posh);

            XeoHandler eoboh = new XeoHandler();
            this.OntologyHandler.Add(eoboh.Uri, eoboh);

            EnvOHandler envoh = new EnvOHandler();
            this.OntologyHandler.Add(envoh.Uri, envoh);

            PEOHandler eoh = new PEOHandler();
            this.OntologyHandler.Add(eoh.Uri, eoh);

            GeneralOntologyHandler goh = new GeneralOntologyHandler();
            this.OntologyHandler.Add(goh.Uri, goh);
            this.generalOntologyHandler = goh;
        }

        #region Fields
        private Dictionary<string, IOntologyHandler> OntologyHandler { get; set; }
        #endregion Fields

        #region dictionary methods
        /// <summary>
        /// Indicate whether or not a handler is known by its uri.
        /// </summary>
        /// <param name="uri"></param>
        /// <returns></returns>
        public bool Contains(string uri)
        {
            return OntologyHandler.ContainsKey(uri);
        }

        public IOntologyHandler Get(string uri)
        {
            if (!OntologyHandler.ContainsKey(uri))
            {
                throw new KeyNotFoundException("Ontology was not found!");
            }
            return OntologyHandler[uri];
        }

        public ICollection<IOntologyHandler> Values
        {
            get
            {
                return OntologyHandler.Values;
            }
        }
        #endregion

        public IOntologyHandler CreateHandler(string uri, string location = "")
        {
            StatusUpdate("Creating new ontology handler.");
            IOntologyHandler ret = null;
            try
            {
                // if the uri is not found (= no dedicated ontology handler)
                // if the uri is the same as for the GeneralOntologyHandler
                // AND as the generalOntologyHandler keys on the location, check for that as well!
                if (!this.Contains(uri) || this.generalOntologyHandler.Uri == uri)
                {
                    if (this.Contains(location))
                    {
                        ret = (IOntologyHandler)this.OntologyHandler[location].Copy();
                    }
                    else
                    {
                        ret = new GeneralOntologyHandler(uri, location);
                        OntologyHandler.Add(location, ret);
                    }
                }
                else
                {
                    var ontologyHandler = this.OntologyHandler[uri].Copy();
                    ret = (IOntologyHandler)ontologyHandler;
                }
            }
            catch (Exception ex)
            {
                // TODO this shouldn't be a catch all. Exceptions that have nothing to do with the handler creation get cuaght as well!
                throw new XemlComponentLoadFailedException(uri, "Handler exists, but loading failed", ex);
            }
            return ret;
        }
    }
}
